/*
 * This file is part of FinVis.
 *
 * FinVis is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FinVis is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with FinVis.  If not, see <http://www.gnu.org/licenses/>
 *
 * @author Stephen Rudolph
 */

#include <QtGui>
#include <QStandardItemModel>
#include <QTableView>
#include <QVBoxLayout>
#include <QApplication>
#include <QMessageBox>

#include <cstdlib>
#include <cmath>

#include "Common.h"
#include "FinPortfolio.h"
#include "FinRecorder.h"
#include "FinView.h"

#define SIDE_PANE_WIDTH 100
#define BOTTOM_PANE_HEIGHT 50
#define TOP_PANE_HEIGHT 50
#define LABEL_X_SPACING 50
#define TICK_WIDTH 3
#define TEXT_HEIGHT 10
#define TEXT_WIDTH 8
#define PRECALCULATED_GAUSSIAN_MAX 187
#define RISK_RESOLUTION 21

extern FinPortfolio scenarioPortfolio;
extern std::vector<InvestmentOption> investmentOptionVector;
extern double **covarianceArray;
extern int finishedScenarios[6];
extern int currentScenario;
extern FinRecorder recorder;

//Normalized +/- 2 standard deviations centered at 100, stddev = 50
double precalculatedGaussian[PRECALCULATED_GAUSSIAN_MAX] = {
    1.0,
    0.999800019999,
    0.999200319915,
    0.998201619028,
    0.996805114543,
    0.995012479193,
    0.992825857904,
    0.990247863518,
    0.98728157159,
    0.983930514273,
    0.980198673307,
    0.97609047214,
    0.971610767189,
    0.966764838271,
    0.961558378238,
    0.955997481833,
    0.950088633803,
    0.943838696301,
    0.937254895613,
    0.930344808241,
    0.923116346387,
    0.915577742862,
    0.907737535487,
    0.899604550995,
    0.891187888504,
    0.882496902585,
    0.873541185979,
    0.86433055201,
    0.854875016725,
    0.845184780824,
    0.835270211411,
    0.825141823623,
    0.814810262169,
    0.804286282838,
    0.793580734016,
    0.782704538242,
    0.771668673875,
    0.760484156884,
    0.749162022825,
    0.737713309034,
    0.726149037074,
    0.714480195486,
    0.702717722868,
    0.690872491319,
    0.678955290289,
    0.666976810858,
    0.654947630486,
    0.642878198238,
    0.630778820547,
    0.618659647503,
    0.606530659713,
    0.59440165575,
    0.582282240202,
    0.570181812338,
    0.558109555417,
    0.54607442664,
    0.53408514776,
    0.522150196358,
    0.510277797796,
    0.498475917837,
    0.48675225596,
    0.475114239335,
    0.463569017496,
    0.452123457673,
    0.440784140805,
    0.429557358211,
    0.418449108913,
    0.407465097613,
    0.39661073329,
    0.38589112843,
    0.375311098851,
    0.364875164124,
    0.354587548561,
    0.344452182767,
    0.334472705718,
    0.324652467358,
    0.314994531697,
    0.305501680368,
    0.29617641665,
    0.287020969911,
    0.278037300453,
    0.269227104747,
    0.260591821013,
    0.252132635143,
    0.243850486927,
    0.235746076556,
    0.227819871398,
    0.220072112995,
    0.212502824275,
    0.205111816952,
    0.197898699084,
    0.19086288277,
    0.184003591968,
    0.177319870398,
    0.170810589523,
    0.164474456577,
    0.158310022622,
    0.152315690614,
    0.146489723462,
    0.140830252054,
    0.135335283237,
    0.130002707737,
    0.124830307996,
    0.119815765915,
    0.114956670486,
    0.110250525304,
    0.105694755941,
    0.101286717172,
    0.0970237000413,
    0.0929029387673,
    0.0889216174594,
    0.0850768766557,
    0.0813658196645,
    0.0777855187066,
    0.0743330208508,
    0.0710053537396,
    0.0677995310992,
    0.0647125580312,
    0.0617414360834,
    0.0588831680978,
    0.0561347628341,
    0.053493239369,
    0.0509556312702,
    0.0485189905474,
    0.0461803913797,
    0.0439369336234,
    0.0417857460999,
    0.0397239896694,
    0.0377488600913,
    0.0358575906764,
    0.0340474547346,
    0.0323157678222,
    0.030659889794,
    0.0290772266655,
    0.0275652322896,
    0.0261214098539,
    0.0247433132052,
    0.023428548005,
    0.0221747727249,
    0.0209796994849,
    0.0198410947444,
    0.0187567798486,
    0.0177246314402,
    0.0167425817401,
    0.0158086187052,
    0.0149207860691,
    0.0140771832716,
    0.013275965285,
    0.0125153423412,
    0.011793579568,
    0.0111089965382,
    0.0104599667409,
    0.00984491697639,
    0.00926232668506,
    0.00871072721186,
    0.00818870101437,
    0.00769488081839,
    0.00722794872657,
    0.00678663528497,
    0.00636971851209,
    0.00597602289501,
    0.00560441835706,
    0.00525381920099,
    0.00492318303171,
    0.00461150966243,
    0.00431784000763,
    0.00404125496639,
    0.00378087429924,
    0.00353585550151,
    0.00330539267609,
    0.00308871540824,
    0.00288508764481,
    0.00269380658042,
    0.00251420155248,
    0.00234563294717,
    0.00218749111818,
    0.00203919531984,
    0.00190019265611,
    0.00176995704686,
    0.00164798821271,
    0.00153381067932,
    0.00142697280233,
    0.00132704581365,
    0.00123362288985,
    0.00114631824333,
    0.00106476623667,
    0.000988620520674};

double precalculatedGaussianCDF[401][2] = {
    {-2.000000,0.000032},
    {-1.990000,0.000034},
    {-1.980000,0.000037},
    {-1.970000,0.000041},
    {-1.960000,0.000044},
    {-1.950000,0.000048},
    {-1.940000,0.000052},
    {-1.930000,0.000057},
    {-1.920000,0.000062},
    {-1.910000,0.000067},
    {-1.900000,0.000072},
    {-1.890000,0.000078},
    {-1.880000,0.000085},
    {-1.870000,0.000092},
    {-1.860000,0.000100},
    {-1.850000,0.000108},
    {-1.840000,0.000117},
    {-1.830000,0.000126},
    {-1.820000,0.000136},
    {-1.810000,0.000147},
    {-1.800000,0.000159},
    {-1.790000,0.000172},
    {-1.780000,0.000185},
    {-1.770000,0.000200},
    {-1.760000,0.000216},
    {-1.750000,0.000233},
    {-1.740000,0.000251},
    {-1.730000,0.000270},
    {-1.720000,0.000291},
    {-1.710000,0.000313},
    {-1.700000,0.000337},
    {-1.690000,0.000362},
    {-1.680000,0.000390},
    {-1.670000,0.000419},
    {-1.660000,0.000450},
    {-1.650000,0.000483},
    {-1.640000,0.000519},
    {-1.630000,0.000557},
    {-1.620000,0.000598},
    {-1.610000,0.000641},
    {-1.600000,0.000687},
    {-1.590000,0.000736},
    {-1.580000,0.000789},
    {-1.570000,0.000845},
    {-1.560000,0.000904},
    {-1.550000,0.000968},
    {-1.540000,0.001035},
    {-1.530000,0.001107},
    {-1.520000,0.001183},
    {-1.510000,0.001264},
    {-1.500000,0.001350},
    {-1.490000,0.001441},
    {-1.480000,0.001538},
    {-1.470000,0.001641},
    {-1.460000,0.001750},
    {-1.450000,0.001866},
    {-1.440000,0.001988},
    {-1.430000,0.002118},
    {-1.420000,0.002256},
    {-1.410000,0.002401},
    {-1.400000,0.002555},
    {-1.390000,0.002718},
    {-1.380000,0.002890},
    {-1.370000,0.003072},
    {-1.360000,0.003264},
    {-1.350000,0.003467},
    {-1.340000,0.003681},
    {-1.330000,0.003907},
    {-1.320000,0.004145},
    {-1.310000,0.004396},
    {-1.300000,0.004661},
    {-1.290000,0.004940},
    {-1.280000,0.005234},
    {-1.270000,0.005543},
    {-1.260000,0.005868},
    {-1.250000,0.006210},
    {-1.240000,0.006569},
    {-1.230000,0.006947},
    {-1.220000,0.007344},
    {-1.210000,0.007760},
    {-1.200000,0.008198},
    {-1.190000,0.008656},
    {-1.180000,0.009137},
    {-1.170000,0.009642},
    {-1.160000,0.010170},
    {-1.150000,0.010724},
    {-1.140000,0.011304},
    {-1.130000,0.011911},
    {-1.120000,0.012545},
    {-1.110000,0.013209},
    {-1.100000,0.013903},
    {-1.090000,0.014629},
    {-1.080000,0.015386},
    {-1.070000,0.016177},
    {-1.060000,0.017003},
    {-1.050000,0.017864},
    {-1.040000,0.018763},
    {-1.030000,0.019699},
    {-1.020000,0.020675},
    {-1.010000,0.021692},
    {-1.000000,0.022750},
    {-0.990000,0.023852},
    {-0.980000,0.024998},
    {-0.970000,0.026190},
    {-0.960000,0.027429},
    {-0.950000,0.028717},
    {-0.940000,0.030054},
    {-0.930000,0.031443},
    {-0.920000,0.032884},
    {-0.910000,0.034380},
    {-0.900000,0.035930},
    {-0.890000,0.037538},
    {-0.880000,0.039204},
    {-0.870000,0.040930},
    {-0.860000,0.042716},
    {-0.850000,0.044565},
    {-0.840000,0.046479},
    {-0.830000,0.048457},
    {-0.820000,0.050503},
    {-0.810000,0.052616},
    {-0.800000,0.054799},
    {-0.790000,0.057053},
    {-0.780000,0.059380},
    {-0.770000,0.061780},
    {-0.760000,0.064255},
    {-0.750000,0.066807},
    {-0.740000,0.069437},
    {-0.730000,0.072145},
    {-0.720000,0.074934},
    {-0.710000,0.077804},
    {-0.700000,0.080757},
    {-0.690000,0.083793},
    {-0.680000,0.086915},
    {-0.670000,0.090123},
    {-0.660000,0.093418},
    {-0.650000,0.096800},
    {-0.640000,0.100273},
    {-0.630000,0.103835},
    {-0.620000,0.107488},
    {-0.610000,0.111232},
    {-0.600000,0.115070},
    {-0.590000,0.119000},
    {-0.580000,0.123024},
    {-0.570000,0.127143},
    {-0.560000,0.131357},
    {-0.550000,0.135666},
    {-0.540000,0.140071},
    {-0.530000,0.144572},
    {-0.520000,0.149170},
    {-0.510000,0.153864},
    {-0.500000,0.158655},
    {-0.490000,0.163543},
    {-0.480000,0.168528},
    {-0.470000,0.173609},
    {-0.460000,0.178786},
    {-0.450000,0.184060},
    {-0.440000,0.189430},
    {-0.430000,0.194895},
    {-0.420000,0.200454},
    {-0.410000,0.206108},
    {-0.400000,0.211855},
    {-0.390000,0.217695},
    {-0.380000,0.223627},
    {-0.370000,0.229650},
    {-0.360000,0.235762},
    {-0.350000,0.241964},
    {-0.340000,0.248252},
    {-0.330000,0.254627},
    {-0.320000,0.261086},
    {-0.310000,0.267629},
    {-0.300000,0.274253},
    {-0.290000,0.280957},
    {-0.280000,0.287740},
    {-0.270000,0.294599},
    {-0.260000,0.301532},
    {-0.250000,0.308538},
    {-0.240000,0.315614},
    {-0.230000,0.322758},
    {-0.220000,0.329969},
    {-0.210000,0.337243},
    {-0.200000,0.344578},
    {-0.190000,0.351973},
    {-0.180000,0.359424},
    {-0.170000,0.366928},
    {-0.160000,0.374484},
    {-0.150000,0.382089},
    {-0.140000,0.389739},
    {-0.130000,0.397432},
    {-0.120000,0.405165},
    {-0.110000,0.412936},
    {-0.100000,0.420740},
    {-0.090000,0.428576},
    {-0.080000,0.436441},
    {-0.070000,0.444330},
    {-0.060000,0.452242},
    {-0.050000,0.460172},
    {-0.040000,0.468119},
    {-0.030000,0.476078},
    {-0.020000,0.484047},
    {-0.010000,0.492022},
    {0.000000,0.500000},
    {0.010000,0.507978},
    {0.020000,0.515953},
    {0.030000,0.523922},
    {0.040000,0.531881},
    {0.050000,0.539828},
    {0.060000,0.547758},
    {0.070000,0.555670},
    {0.080000,0.563559},
    {0.090000,0.571424},
    {0.100000,0.579260},
    {0.110000,0.587064},
    {0.120000,0.594835},
    {0.130000,0.602568},
    {0.140000,0.610261},
    {0.150000,0.617911},
    {0.160000,0.625516},
    {0.170000,0.633072},
    {0.180000,0.640576},
    {0.190000,0.648027},
    {0.200000,0.655422},
    {0.210000,0.662757},
    {0.220000,0.670031},
    {0.230000,0.677242},
    {0.240000,0.684386},
    {0.250000,0.691462},
    {0.260000,0.698468},
    {0.270000,0.705401},
    {0.280000,0.712260},
    {0.290000,0.719043},
    {0.300000,0.725747},
    {0.310000,0.732371},
    {0.320000,0.738914},
    {0.330000,0.745373},
    {0.340000,0.751748},
    {0.350000,0.758036},
    {0.360000,0.764238},
    {0.370000,0.770350},
    {0.380000,0.776373},
    {0.390000,0.782305},
    {0.400000,0.788145},
    {0.410000,0.793892},
    {0.420000,0.799546},
    {0.430000,0.805105},
    {0.440000,0.810570},
    {0.450000,0.815940},
    {0.460000,0.821214},
    {0.470000,0.826391},
    {0.480000,0.831472},
    {0.490000,0.836457},
    {0.500000,0.841345},
    {0.510000,0.846136},
    {0.520000,0.850830},
    {0.530000,0.855428},
    {0.540000,0.859929},
    {0.550000,0.864334},
    {0.560000,0.868643},
    {0.570000,0.872857},
    {0.580000,0.876976},
    {0.590000,0.881000},
    {0.600000,0.884930},
    {0.610000,0.888768},
    {0.620000,0.892512},
    {0.630000,0.896165},
    {0.640000,0.899727},
    {0.650000,0.903200},
    {0.660000,0.906582},
    {0.670000,0.909877},
    {0.680000,0.913085},
    {0.690000,0.916207},
    {0.700000,0.919243},
    {0.710000,0.922196},
    {0.720000,0.925066},
    {0.730000,0.927855},
    {0.740000,0.930563},
    {0.750000,0.933193},
    {0.760000,0.935745},
    {0.770000,0.938220},
    {0.780000,0.940620},
    {0.790000,0.942947},
    {0.800000,0.945201},
    {0.810000,0.947384},
    {0.820000,0.949497},
    {0.830000,0.951543},
    {0.840000,0.953521},
    {0.850000,0.955435},
    {0.860000,0.957284},
    {0.870000,0.959070},
    {0.880000,0.960796},
    {0.890000,0.962462},
    {0.900000,0.964070},
    {0.910000,0.965620},
    {0.920000,0.967116},
    {0.930000,0.968557},
    {0.940000,0.969946},
    {0.950000,0.971283},
    {0.960000,0.972571},
    {0.970000,0.973810},
    {0.980000,0.975002},
    {0.990000,0.976148},
    {1.000000,0.977250},
    {1.010000,0.978308},
    {1.020000,0.979325},
    {1.030000,0.980301},
    {1.040000,0.981237},
    {1.050000,0.982136},
    {1.060000,0.982997},
    {1.070000,0.983823},
    {1.080000,0.984614},
    {1.090000,0.985371},
    {1.100000,0.986097},
    {1.110000,0.986791},
    {1.120000,0.987455},
    {1.130000,0.988089},
    {1.140000,0.988696},
    {1.150000,0.989276},
    {1.160000,0.989830},
    {1.170000,0.990358},
    {1.180000,0.990863},
    {1.190000,0.991344},
    {1.200000,0.991802},
    {1.210000,0.992240},
    {1.220000,0.992656},
    {1.230000,0.993053},
    {1.240000,0.993431},
    {1.250000,0.993790},
    {1.260000,0.994132},
    {1.270000,0.994457},
    {1.280000,0.994766},
    {1.290000,0.995060},
    {1.300000,0.995339},
    {1.310000,0.995604},
    {1.320000,0.995855},
    {1.330000,0.996093},
    {1.340000,0.996319},
    {1.350000,0.996533},
    {1.360000,0.996736},
    {1.370000,0.996928},
    {1.380000,0.997110},
    {1.390000,0.997282},
    {1.400000,0.997445},
    {1.410000,0.997599},
    {1.420000,0.997744},
    {1.430000,0.997882},
    {1.440000,0.998012},
    {1.450000,0.998134},
    {1.460000,0.998250},
    {1.470000,0.998359},
    {1.480000,0.998462},
    {1.490000,0.998559},
    {1.500000,0.998650},
    {1.510000,0.998736},
    {1.520000,0.998817},
    {1.530000,0.998893},
    {1.540000,0.998965},
    {1.550000,0.999032},
    {1.560000,0.999096},
    {1.570000,0.999155},
    {1.580000,0.999211},
    {1.590000,0.999264},
    {1.600000,0.999313},
    {1.610000,0.999359},
    {1.620000,0.999402},
    {1.630000,0.999443},
    {1.640000,0.999481},
    {1.650000,0.999517},
    {1.660000,0.999550},
    {1.670000,0.999581},
    {1.680000,0.999610},
    {1.690000,0.999638},
    {1.700000,0.999663},
    {1.710000,0.999687},
    {1.720000,0.999709},
    {1.730000,0.999730},
    {1.740000,0.999749},
    {1.750000,0.999767},
    {1.760000,0.999784},
    {1.770000,0.999800},
    {1.780000,0.999815},
    {1.790000,0.999828},
    {1.800000,0.999841},
    {1.810000,0.999853},
    {1.820000,0.999864},
    {1.830000,0.999874},
    {1.840000,0.999883},
    {1.850000,0.999892},
    {1.860000,0.999900},
    {1.870000,0.999908},
    {1.880000,0.999915},
    {1.890000,0.999922},
    {1.900000,0.999928},
    {1.910000,0.999933},
    {1.920000,0.999938},
    {1.930000,0.999943},
    {1.940000,0.999948},
    {1.950000,0.999952},
    {1.960000,0.999956},
    {1.970000,0.999959},
    {1.980000,0.999963},
    {1.990000,0.999966},
    {2.000000,0.999968}};

using namespace std;

FinView::FinView(enum display displayType, QWidget *parent) : QWidget(parent)
{
    this->displayType = displayType;
    widgetSize.setHeight(MAIN_VIEW_HEIGHT);
    widgetSize.setWidth(MAIN_VIEW_WIDTH);
    minBound = 500;
    maxBound = 1500;
    sampleOutcome = 0;

    sampleOutcomeShown = false;
    showGradient = true;
    showSelected = true;
    showUnselected = true;
    showLiquidAssets = true;
    showBoundary = true;
    showResult = true;

    if(displayType == TABLE_VIEW)
    {
        wealthTableView = new QTableView(this);
        wealthTableView->setSelectionMode(QAbstractItemView::NoSelection);
        wealthTableView->setSortingEnabled(true);

        model = new QStandardItemModel(MAX_YEARS + 1, 5, this);
        model->setHeaderData(0, Qt::Horizontal, "Year");
        model->setHeaderData(1, Qt::Horizontal, "Expected Net Wealth");
        model->setHeaderData(2, Qt::Horizontal, QString::fromUtf8("-2 Standard Deviations (σ)"));
        model->setHeaderData(3, Qt::Horizontal, QString::fromUtf8("+2 Standard Deviations (σ)"));
        model->setHeaderData(4, Qt::Horizontal, "Funds Available");

        wealthTableView->setModel(model);
        wealthTableView->sortByColumn(0, Qt::AscendingOrder);
        wealthTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
        
        riskTableView = new QTableView(this);
        riskTableView->setSelectionMode(QAbstractItemView::NoSelection);
        riskTableView->setSortingEnabled(false);
        
        riskModel = new QStandardItemModel(12, RISK_RESOLUTION, this);
        for (int i = 0; i < RISK_RESOLUTION; i++)
        {
            riskModel->setHeaderData(i, Qt::Horizontal, QString::number(-2.0 + (i * 4.0 / (RISK_RESOLUTION - 1)), 'f', 2) +
                                     QString::fromUtf8("σ"));
        }

        for (int i = 1; i <= 12; i++)
        {
            riskModel->setHeaderData(i - 1, Qt::Vertical,
                                     QDate::shortMonthName(i, QDate::StandaloneFormat));
        }
        
        riskTableView->setModel(riskModel);
        riskTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
        
        QVBoxLayout *wealthTableLayout = new QVBoxLayout;
        wealthTableLayout->addWidget(wealthTableView);
        wealthTableLayout->addWidget(riskTableView);

        this->setLayout(wealthTableLayout);
    }
    else
    {
        setBackgroundRole(QPalette::Base);
        setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

        defaultPalette = QApplication::palette();
        defaultPalette.setColor(QPalette::Window, QColor::fromRgb(255, 255, 255));
        this->setPalette(defaultPalette);
        this->setAutoFillBackground(true);

        if (displayType == MINI_VIEW || displayType == MINI_VIEW_OPTIMISTIC_ONLY || displayType ==
            MINI_VIEW_PESSIMISTIC_ONLY)
        {
            sampleOutcomeShown = false;
            showGradient = false;
            showSelected = false;
            showUnselected = false;
            showLiquidAssets = true;
            showBoundary = true;
            showResult = true;

            sidePaneWidth = 5;
            bottomPaneHeight = 5;
            topPaneHeight = 5;
        }
        else
        {
            sidePaneWidth = SIDE_PANE_WIDTH;
            bottomPaneHeight = BOTTOM_PANE_HEIGHT;
            topPaneHeight = TOP_PANE_HEIGHT;
        }
    }

    connect(&scenarioPortfolio, SIGNAL(selectionChanged()), this, SLOT(update()));
    connect(&scenarioPortfolio, SIGNAL(portfolioDataChanged()), this, SLOT(update()));
}

QSize FinView::minimumSizeHint() const
{
    if (displayType == MINI_VIEW || displayType == MINI_VIEW_OPTIMISTIC_ONLY || displayType ==
        MINI_VIEW_PESSIMISTIC_ONLY)
        return QSize(MINIMUM_SIZE, MINIMUM_SIZE);
    else
        return QSize(MINIMUM_SIZE * 2, MINIMUM_SIZE * 2);
}

QSize FinView::sizeHint() const
{
    if (displayType == MINI_VIEW || displayType == MINI_VIEW_OPTIMISTIC_ONLY || displayType ==
        MINI_VIEW_PESSIMISTIC_ONLY)
        return minimumSizeHint();
    else
        return widgetSize;
}

void FinView::drawBox(QPainter *painter)
{
    QBrush white = QBrush(QColor::fromRgb(255, 255, 255, 255));
    int axisHeight = height() - bottomPaneHeight - topPaneHeight;

    painter->fillRect(0, -axisHeight / 2 - topPaneHeight / 2 - bottomPaneHeight / 2, 
                      width(), topPaneHeight, white);
    painter->fillRect(0, axisHeight / 2 - topPaneHeight / 2, width(), bottomPaneHeight, white);
}

void FinView::drawAxes(QPainter *painter)
{
    painter->setPen(QPen(defaultPalette.color(QPalette::WindowText), 2, Qt::SolidLine, Qt::FlatCap,
                         Qt::MiterJoin));

    int axisHeight = height() - bottomPaneHeight - topPaneHeight;
    int tickCountY = 10;
    int labelYSpacing = axisHeight / tickCountY;
    int axisWidth = width() - 2 * sidePaneWidth;
    int top = -axisHeight / 2 - topPaneHeight / 2;
    int xSpacing;
    if(MAX_YEARS < LABEL_X_SPACING)
        xSpacing = axisWidth / MAX_YEARS;
    else
        xSpacing = LABEL_X_SPACING;
    int plotWidth = (width() - 2 * sidePaneWidth) - ((width() - 2 * sidePaneWidth) % xSpacing);

    int tickCountX = axisWidth / xSpacing;
    int left = sidePaneWidth;
    int right = width() - sidePaneWidth - (axisWidth % xSpacing);

    //Left Vertical Axis
    painter->drawLine(sidePaneWidth, -axisHeight / 2 - topPaneHeight / 2, sidePaneWidth,
                      axisHeight / 2 - topPaneHeight / 2);
    for(int i = 0; i <= tickCountY; i++)
    {
        painter->drawLine(sidePaneWidth - TICK_WIDTH,
                          -axisHeight / 2 + (i * labelYSpacing) - topPaneHeight / 2,
                          sidePaneWidth + TICK_WIDTH,
                          -axisHeight / 2 + (i * labelYSpacing) - topPaneHeight / 2);
    }

    //Right Vertical Axis
    painter->drawLine(right, -axisHeight / 2 - topPaneHeight / 2,
                      right, axisHeight / 2 - topPaneHeight / 2);
    for(int i = 0; i <= tickCountY; i++)
    {
        painter->drawLine(right - TICK_WIDTH, -axisHeight / 2 + (i * labelYSpacing) - topPaneHeight / 2,
                          right + TICK_WIDTH, -axisHeight / 2 + (i * labelYSpacing) - topPaneHeight / 2);
    }

    if (displayType != MINI_VIEW && displayType != MINI_VIEW_OPTIMISTIC_ONLY && displayType !=
        MINI_VIEW_PESSIMISTIC_ONLY)
    {
        painter->setPen(QPen(QColor::fromRgb(0, 0, 0, 60), 2, Qt::DashLine, Qt::FlatCap, Qt::MiterJoin));

        //Background ticks
        for(int i = 0; i <= tickCountY; i++)
        {
            painter->drawLine(sidePaneWidth, -axisHeight / 2 + (i * labelYSpacing) - topPaneHeight / 2,
                              right, -axisHeight / 2 + (i * labelYSpacing) - topPaneHeight / 2);
        }
    }

    painter->setPen(QPen(defaultPalette.color(QPalette::WindowText), 2, Qt::SolidLine, Qt::FlatCap,
                         Qt::MiterJoin));

    //Horizontal Axis
    painter->drawLine(sidePaneWidth, axisHeight / 2 - topPaneHeight / 2,
                      right, axisHeight / 2 - topPaneHeight / 2);
    for(int i = 0; i <= tickCountX; i++)
    {
        painter->drawLine(sidePaneWidth + i * xSpacing, axisHeight / 2 - topPaneHeight / 2 - TICK_WIDTH,
                          sidePaneWidth + i * xSpacing, axisHeight / 2 - topPaneHeight / 2 + TICK_WIDTH);
    }

    //Draw y=0 line
    if(minBound < 0 && maxBound > 0)
    {
        painter->setPen(Qt::DashLine);
        painter->drawLine(left, top + axisHeight * maxBound / (maxBound - minBound),
                          left + plotWidth, top + axisHeight * maxBound / (maxBound - minBound));
        painter->setPen(Qt::SolidLine);
    }
}

void FinView::drawAxesLabels(QPainter *painter)
{
    int axisHeight = height() - bottomPaneHeight - topPaneHeight;
    int labelCountY = 10;
    int labelYSpacing = axisHeight / labelCountY;

    int axisWidth = width() - 2 * sidePaneWidth;
    int top = -axisHeight / 2 - topPaneHeight / 2;

    int xSpacing;
    if(MAX_YEARS < LABEL_X_SPACING)
        xSpacing = axisWidth / MAX_YEARS;
    else
        xSpacing = LABEL_X_SPACING;

    int labelCountX = axisWidth / xSpacing;
    int right = width() - sidePaneWidth - (axisWidth % ((int) xSpacing));

    char label[20];

    //Y axes labels
    for(int i = 0; i <= labelCountY; i++)
    {
        sprintf(label, "$%i", (int) (maxBound - (i * (maxBound - minBound)) / labelCountY));
        painter->drawText(0, top + (i * labelYSpacing) + (TEXT_HEIGHT / 2), tr(label));
        painter->drawText(right + TICK_WIDTH, top + (i * labelYSpacing) + (TEXT_HEIGHT / 2), tr(label));
    }

    //X axis labels
    QDate startDate = QDate(START_YEAR, 1, 1);
    for(int i = 0; i <= labelCountX; i++)
    {
        QString yearText = "";

        if(i == 0)
            yearText = "Now";
        else if(i == 1)
            yearText = "1 Year Later";
        else
            yearText = QString::number(i) + " Years Later";

        painter->drawText(sidePaneWidth + i * xSpacing - TEXT_WIDTH * 2,
                          axisHeight / 2 - topPaneHeight / 2 + TICK_WIDTH + TEXT_HEIGHT, yearText);
        //painter->drawText(sidePaneWidth + i * xSpacing - TEXT_WIDTH * 2,
        //                  axisHeight / 2 - topPaneHeight / 2 + TICK_WIDTH + TEXT_HEIGHT,
        //                  startDate.addYears(i * ((float) MAX_YEARS / labelCountX)).toString("yyyy"));
    }
}

void FinView::plotInvestments(QPainter *painter)
{
    QPen defaultPen(Qt::SolidPattern, 0, Qt::SolidLine, Qt::FlatCap);
    QPen widePen(Qt::SolidPattern, 0.1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
    painter->setPen(defaultPen);

    unsigned int i;

    //Draw Gradients
    if(this->showGradient && (this->displayType == FinView::DEFAULT_VIEW ||
                              this->displayType == FinView::OPTIMISTIC_ONLY || 
                              this->displayType == FinView::PESSIMISTIC_ONLY))
    {
        painter->setRenderHint(QPainter::Antialiasing, false);
        painter->resetTransform();
        painter->translate(0, height() / 2);

        int left = sidePaneWidth;
        int axisWidth = width() - 2 * sidePaneWidth;
        int xSpacing;
        if(MAX_YEARS < LABEL_X_SPACING)
            xSpacing = axisWidth / MAX_YEARS;
        else
            xSpacing = LABEL_X_SPACING;

        float plotWidth = (width() - 2 * sidePaneWidth) -
                          ((width() - 2 * sidePaneWidth) % xSpacing);
        int plotHeight = height() - bottomPaneHeight - topPaneHeight;
        int top = -plotHeight / 2 - topPaneHeight / 2;
        int bottom = top + plotHeight;// height() / 2 - bottomPaneHeight;

        double magnitude, gray, travelled, distance = ((double) plotWidth / MAX_YEARS), m, ml, mh;
        int drawX;

        for(i = 0; i < MAX_YEARS; i++)
        {
            for(int x = 0; x < distance; x++)
            {
                travelled = x / distance;
                ml = scenarioPortfolio.pessimisticWealth(i) * (1.0 - travelled) + scenarioPortfolio.pessimisticWealth(i + 1) * travelled;
                m = scenarioPortfolio.expectedWealth(i) * (1.0 - travelled) + scenarioPortfolio.expectedWealth(i + 1) * travelled;
                mh = scenarioPortfolio.optimisticWealth(i) * (1.0 - travelled) + scenarioPortfolio.optimisticWealth(i + 1) * travelled;

                int minusTwoStdDev = bottom - ((ml - minBound) / (maxBound - minBound)) * plotHeight;
                int zeroStdDev = bottom - ((m - minBound) / (maxBound - minBound)) * plotHeight;
                int plusTwoStdDev = bottom - ((mh - minBound) / (maxBound - minBound)) * plotHeight;

                int upperBound = plusTwoStdDev;
                int lowerBound = minusTwoStdDev;

                if (this->displayType == FinView::OPTIMISTIC_ONLY)
                {
                    lowerBound = zeroStdDev;
                }
                else if (this->displayType == FinView::PESSIMISTIC_ONLY)
                {
                    upperBound = zeroStdDev;
                }

                for(int y = lowerBound; y >= upperBound; y--)
                {
                    if(zeroStdDev != plusTwoStdDev && zeroStdDev != minusTwoStdDev)
                    {
                        if(y > zeroStdDev)
                        {
                            magnitude = 100.0 * ((y - zeroStdDev) / ((double) minusTwoStdDev - zeroStdDev));
                            gray = 200.0 * precalculatedGaussian[(int) magnitude];
                        }
                        else
                        {
                            magnitude = 100.0 * ((zeroStdDev - y) / ((double) zeroStdDev - plusTwoStdDev));
                            gray = 200.0 * precalculatedGaussian[(int) magnitude];
                        }
                    }
                    else
                        gray = 200;

                    //printf("G: %f\n", gray);
                    painter->setPen(QColor::fromRgb(0, 0, 0, (int) gray));
                    drawX = x + left + i * ((float) plotWidth / MAX_YEARS);
                    painter->drawPoint(drawX, y);
                }
            }
        }
        painter->setRenderHint(QPainter::Antialiasing, true);
        /*double magnitude, gray, travelled, distance = MAX_YEARS, m, ml, mh;
        double y_increment, x_increment;
        QBrush grayBrush(Qt::SolidPattern);
        painter->setPen(Qt::NoPen);

        int axisWidth = width() - 2 * sidePaneWidth;
        int xSpacing;
        if(MAX_YEARS < LABEL_X_SPACING)
            xSpacing = axisWidth / MAX_YEARS;
        else
            xSpacing = LABEL_X_SPACING;

        float plotWidth = (width() - 2 * sidePaneWidth) -
                          ((width() - 2 * sidePaneWidth) % xSpacing);
        int plotHeight = height() - bottomPaneHeight - topPaneHeight;
        plotHeight -= plotHeight % LABEL_Y_SPACING;

        x_increment = 1.0 / plotWidth;
        for(i = 0; i < MAX_YEARS; i++)
        {
            for(double x = 0; x < 1; x += x_increment)
            {
                travelled = x / distance;
                ml = scenarioPortfolio.pessimisticWealth(i) *
                     (1.0 - travelled) +
                     scenarioPortfolio.pessimisticWealth(i + 1) * travelled;
                m = scenarioPortfolio.expectedWealth(i) * (1.0 - travelled) +
                    scenarioPortfolio.expectedWealth(i + 1) * travelled;
                mh = scenarioPortfolio.optimisticWealth(i) * (1.0 - travelled) +
                     scenarioPortfolio.optimisticWealth(i + 1) * travelled;

                y_increment = (maxBound - minBound) / (float) plotHeight;

                for(double y = ml; y <= mh; y += y_increment)
                {
                    if(m != mh && m != ml)
                    {
                        if(y > m)
                        {
                            magnitude = 100.0 * ((y - m) / (mh - m));
                            gray = 200.0 *
                                   precalculatedGaussian[(int) magnitude];
                        }
                        else
                        {
                            magnitude = 100.0 * ((m - y) / (m - ml));
                            gray = 200.0 *
                                   precalculatedGaussian[(int) magnitude];
                        }
                    }
                    else
                        gray = 200;

                    //printf("G: %f\n", gray);

                    grayBrush.setColor(QColor::fromRgb(0, 0, 0, (int) gray));
                    painter->setBrush(grayBrush);
                    painter->drawPoint(QPointF(x + i, y));
                }
            }
        }
        painter->setRenderHint(QPainter::Antialiasing, true);*/
        painter->setBrush(Qt::NoBrush);
        this->transformPainterForGraph(painter);
    }

    map<unsigned int, FinInvestment> investments =
            scenarioPortfolio.investments();

    //Draw high and low estimates
    if (this->showBoundary)
    {
        QPen bluePen(Qt::SolidPattern, 0, Qt::SolidLine, Qt::FlatCap);
        bluePen.setColor(QColor(0, 0, 255, 150));

        painter->setPen(bluePen);

        for(map<unsigned int, FinInvestment>::iterator
            investmentIterator = investments.begin();
            investmentIterator != investments.end(); ++investmentIterator)
        {
            for(i = 0; i < MAX_YEARS; i++)
            {
                int k = investmentIterator->second.orderIndex();

                if(this->displayType == FinView::DEFAULT_VIEW ||
                   (k == 0 && (this->displayType == FinView::SIMPLE_VIEW ||
                               this->displayType == FinView::MINI_VIEW)))
                {
                    painter->drawLine(i,
                                      scenarioPortfolio.pessimisticWealth(i,investmentIterator->first),
                                      i + 1,
                                      scenarioPortfolio.pessimisticWealth(i + 1,
                                                                          investmentIterator->first));
                    painter->drawLine(i,
                                      scenarioPortfolio.optimisticWealth(i, investmentIterator->first),
                                      i + 1,
                                      scenarioPortfolio.optimisticWealth(i + 1,
                                                                         investmentIterator->first));
                }
                else if(this->displayType == FinView::OPTIMISTIC_ONLY ||
                        (k == 0 && this->displayType == FinView::MINI_VIEW_OPTIMISTIC_ONLY))
                {
                    painter->drawLine(i,
                                      scenarioPortfolio.optimisticWealth(i, investmentIterator->first),
                                      i + 1,
                                      scenarioPortfolio.optimisticWealth(i + 1,
                                                                         investmentIterator->first));
                }
                else if(this->displayType == FinView::PESSIMISTIC_ONLY ||
                        (k == 0 && this->displayType == FinView::MINI_VIEW_PESSIMISTIC_ONLY))
                {
                    painter->drawLine(i,
                                      scenarioPortfolio.pessimisticWealth(i,investmentIterator->first),
                                      i + 1,
                                      scenarioPortfolio.pessimisticWealth(i + 1,
                                                                          investmentIterator->first));
                }

                painter->setPen(widePen);
                painter->drawLine(i, scenarioPortfolio.expectedWealth(i),
                                  i + 1, scenarioPortfolio.expectedWealth(i + 1));
                painter->setPen(bluePen);
            }
        }
        painter->setPen(Qt::NoPen);
    }

    QPolygon polygon;
    int points[8];
    //Highlighting
    if((this->displayType == FinView::DEFAULT_VIEW ||
        this->displayType == FinView::OPTIMISTIC_ONLY ||
        this->displayType == FinView::PESSIMISTIC_ONLY) && scenarioPortfolio.investmentCount() > 0)
    {
        if (this->showSelected)
        {
            for(i = 0; i < MAX_YEARS; i++)
            {
                QColor yellow(255, 255, 0, 100);
                QBrush highlightBrush(Qt::SolidPattern);
                highlightBrush.setColor(yellow);

                //Optimistic
                if (this->displayType == FinView::DEFAULT_VIEW ||
                    this->displayType == FinView::OPTIMISTIC_ONLY)
                {
                    points[0] = points[2] = i;
                    points[4] = points[6] = i + 1;

                    if(scenarioPortfolio.selectedInvestment()->orderIndex() + 1 <
                       scenarioPortfolio.investmentCount())
                    {
                        points[1] = scenarioPortfolio.optimisticWealth(
                                i, scenarioPortfolio.nextID());
                    }
                    else
                    {
                        points[1] = scenarioPortfolio.expectedWealth(i);
                    }

                    points[3] = scenarioPortfolio.optimisticWealth(
                            i, scenarioPortfolio.selectedID());
                    points[5] = scenarioPortfolio.optimisticWealth(
                            i + 1, scenarioPortfolio.selectedID());

                    if(scenarioPortfolio.selectedInvestment()->orderIndex() + 1 <
                       scenarioPortfolio.investmentCount())
                    {
                        points[7] = scenarioPortfolio.optimisticWealth(
                                i + 1, scenarioPortfolio.nextID());
                    }
                    else
                    {
                        points[7] = scenarioPortfolio.expectedWealth(i + 1);
                    }

                    polygon.setPoints(4, points);
                    QPainterPath path;
                    path.addPolygon(polygon);
                    painter->fillPath(path, highlightBrush);
                }

                //Pessimistic
                if (this->displayType == FinView::DEFAULT_VIEW ||
                    this->displayType == FinView::PESSIMISTIC_ONLY)
                {
                    points[0] = points[2] = i;
                    points[4] = points[6] = i + 1;

                    points[1] = scenarioPortfolio.pessimisticWealth(
                            i, scenarioPortfolio.selectedID());
                    if(scenarioPortfolio.selectedInvestment()->orderIndex() + 1 <
                       scenarioPortfolio.investmentCount())
                    {
                        points[3] = scenarioPortfolio.pessimisticWealth(
                                i, scenarioPortfolio.nextID());
                    }
                    else
                    {
                        points[3] = scenarioPortfolio.expectedWealth(i);
                    }

                    if(scenarioPortfolio.selectedInvestment()->orderIndex() + 1 <
                       scenarioPortfolio.investmentCount())
                    {
                        points[5] = scenarioPortfolio.pessimisticWealth(
                                i + 1, scenarioPortfolio.nextID());
                    }
                    else
                    {
                        points[5] = scenarioPortfolio.expectedWealth(i + 1);
                    }

                    points[7] = scenarioPortfolio.pessimisticWealth(
                            i + 1, scenarioPortfolio.selectedID());

                    polygon.setPoints(4, points);
                    QPainterPath path2;
                    path2.addPolygon(polygon);
                    painter->fillPath(path2, highlightBrush);
                }
            }

            painter->setBrush(Qt::NoBrush);
        }

        //Draw markers
        float x_radius = MAX_YEARS / 20.0;
        float y_radius = (maxBound - minBound) / 20.0;
        QPen markerPen(Qt::SolidPattern, 0, Qt::SolidLine, Qt::FlatCap);
        markerPen.setColor(Qt::red);
        painter->setPen(markerPen);

        if (this->showUnselected)
        {
            for(map<unsigned int, FinInvestment>::iterator
                investmentIterator = investments.begin();
                investmentIterator != investments.end(); ++investmentIterator)
            {
                FinInvestment investment = investmentIterator->second;
                int year = investment.startDate().year() - START_YEAR;
                float x = year - x_radius / 2.0;
                float y = scenarioPortfolio.expectedWealth(year) -
                          y_radius / 2.0;
                QRectF arcBox(x, y, x_radius, y_radius);
                painter->drawEllipse(arcBox);
            }
        }

        QBrush markerBrush(Qt::SolidPattern);
        markerBrush.setColor(Qt::red);

        if (this->showSelected)
        {
            painter->setBrush(markerBrush);
            int year = scenarioPortfolio.startYearForInvestment(
                    scenarioPortfolio.selectedID()) - START_YEAR;

            float x = (float) year - x_radius / 2.0;
            float y = (float) scenarioPortfolio.expectedWealth(year) -
                      y_radius / 2.0;
            QRectF arcBox(x, y, x_radius, y_radius);
            painter->drawEllipse(arcBox);
            painter->setBrush(Qt::NoBrush);
        }
    }

    //Show Sample Outcome
    QPen yellowPen(Qt::SolidPattern, 0, Qt::SolidLine, Qt::FlatCap);
    yellowPen.setColor(Qt::yellow);

    double sampleResultValue;
    if(sampleOutcome == 0 || scenarioPortfolio.investmentCount() == 0)
    {
        sampleResultValue = scenarioPortfolio.expectedWealth(MAX_YEARS);
    }
    else if(sampleOutcome < 0)
    {
        sampleResultValue = -sampleOutcome *
                            scenarioPortfolio.pessimisticWealth(MAX_YEARS) +
                            (1.0 + sampleOutcome) *
                            scenarioPortfolio.expectedWealth(MAX_YEARS);
    }
    else if(sampleOutcome > 0)
    {
        sampleResultValue = sampleOutcome *
                            scenarioPortfolio.optimisticWealth(MAX_YEARS) +
                            (1.0 - sampleOutcome) *
                            scenarioPortfolio.expectedWealth(MAX_YEARS);
    }

    if(this->showResult && sampleOutcomeShown)
    {
        painter->setPen(yellowPen);
        painter->drawLine(MAX_YEARS - 0.3, sampleResultValue,
                          MAX_YEARS, sampleResultValue);
        painter->setPen(defaultPen);
        painter->drawLine(MAX_YEARS - 0.3, sampleResultValue,
                          MAX_YEARS, sampleResultValue);
    }

    painter->setBrush(Qt::NoBrush);
}

//Plot liquid assets
void FinView::plotLiquidAssets(QPainter *painter)
{
    if (this->showLiquidAssets)
    {        
        QPen liquidPen(Qt::Dense2Pattern, 0, Qt::SolidLine, Qt::FlatCap);
        liquidPen.setColor(QColor(255, 0, 255, 100));
        painter->setPen(liquidPen);

        for(int i = 0; i < MAX_YEARS; i++)
        {
            painter->drawLine(i, scenarioPortfolio.liquidAssets(i),
                              i + 1, scenarioPortfolio.liquidAssets(i));
        }
    }
}

void FinView::transformPainterForGraph(QPainter *painter)
{
    int left = sidePaneWidth;
    int axisWidth = width() - 2 * sidePaneWidth;
    int xSpacing;
    if(MAX_YEARS < LABEL_X_SPACING)
        xSpacing = axisWidth / MAX_YEARS;
    else
        xSpacing = LABEL_X_SPACING;

    float plotWidth = (width() - 2 * sidePaneWidth) -
                      ((width() - 2 * sidePaneWidth) % xSpacing);
    int plotHeight = height() - bottomPaneHeight - topPaneHeight;
    int top = -plotHeight / 2 - topPaneHeight / 2;
    int bottom = top + plotHeight;// height() / 2 - bottomPaneHeight;

    painter->resetTransform();
    painter->translate(0, height() / 2);
    painter->translate(left, bottom);
    painter->scale((float) plotWidth / (float) MAX_YEARS,
                   plotHeight / (float) -(maxBound - minBound));
    painter->translate(0, -minBound);
}

void FinView::paintEvent(QPaintEvent *)
{
    if(displayType == TABLE_VIEW)
    {
        model->removeRows(0, model->rowCount());

        model->setRowCount(MAX_YEARS + 1);
        for(unsigned int index = 0; index <= MAX_YEARS; index++)
        {
            if(index == 0)
                model->setData(model->index(index, 0, QModelIndex()), "Now");
            else if(index == 1)
                model->setData(model->index(index, 0, QModelIndex()), "1 Year Later");
            else
                model->setData(model->index(index, 0, QModelIndex()), QString::number(index) + " Years Later");

            model->setData(model->index(index, 1, QModelIndex()), tr("$") + QString::number(scenarioPortfolio.expectedWealth(index)));
            model->setData(model->index(index, 2, QModelIndex()), tr("$") + QString::number(scenarioPortfolio.pessimisticWealth(index)));
            model->setData(model->index(index, 3, QModelIndex()), tr("$") + QString::number(scenarioPortfolio.optimisticWealth(index)));
            model->setData(model->index(index, 4, QModelIndex()), tr("$") + QString::number(scenarioPortfolio.liquidAssets(index)));
        }

        wealthTableView->setModel(model);
        wealthTableView->reset();
        //wealthTableView->sortByColumn(0, Qt::AscendingOrder);
        wealthTableView->resizeColumnsToContents();
        wealthTableView->setFixedHeight(wealthTableView->sizeHint().height());
        /*for(int i = 0; i < 5; i++)
            wealthTableView->setColumnWidth(i, (this->width() / 5.0) - 15);
        wealthTableView->setMinimumSize(wealthTableView->sizeHint());*/

        double initialAmount = scenarioPortfolio.expectedWealth(0);
        double finalOptimistic = scenarioPortfolio.optimisticWealth(MAX_YEARS, 0);
        double finalPessimistic = scenarioPortfolio.pessimisticWealth(MAX_YEARS, 0);
        
        //Linear interpolation for the risk
        for (unsigned int month = 1; month <= 12; month++)
        {
            double spread = (month / 12.0) * (finalOptimistic - finalPessimistic);
            double monthlyPessimistic = ((finalPessimistic - initialAmount) / 12.0) * month + initialAmount;
            
            for (unsigned int column = 0; column < RISK_RESOLUTION; column++)
                riskModel->setData(riskModel->index(month - 1, column, QModelIndex()), tr("$") + QString::number((column / (RISK_RESOLUTION - 1.0)) * spread + monthlyPessimistic, 'f', 0));
        }
        
        riskTableView->setModel(riskModel);
        riskTableView->reset();
        riskTableView->resizeColumnsToContents();
    }
    else
    {
        if (displayType == MINI_VIEW ||
            displayType == FinView::MINI_VIEW_OPTIMISTIC_ONLY ||
            displayType == FinView::MINI_VIEW_PESSIMISTIC_ONLY)
        {
            int minAmount = 0;
            int maxAmount = 1000;
            int pessimistic, optimistic;
            for (int year = 0; year <= MAX_YEARS; year++)
            {
                pessimistic = scenarioPortfolio.pessimisticWealth(year);
                optimistic = scenarioPortfolio.optimisticWealth(year);

                if (pessimistic < minAmount)
                    minAmount = pessimistic;
                if (optimistic > maxAmount)
                    maxAmount = optimistic;
            }

            //Add buffer
            int spread = maxAmount - minAmount;
            maxBound = maxAmount + spread * 0.1;
            minBound = minAmount - spread * 0.1;
        }

        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing, true);
        painter.resetTransform();
        //painter.setPen(QPen(defaultPalette.color(QPalette::WindowText), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));

        //if(displayType == DEFAULT_VIEW)
        this->transformPainterForGraph(&painter);
        this->plotInvestments(&painter);
        //else if(displayType == LIQUID_ASSETS)
        this->plotLiquidAssets(&painter);

        painter.resetTransform();
        painter.translate(0, height() / 2);
        painter.setPen(QPen(defaultPalette.color(QPalette::WindowText), 2, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin));

        this->drawAxes(&painter);

        if (displayType != MINI_VIEW &&
            displayType != FinView::MINI_VIEW_OPTIMISTIC_ONLY &&
            displayType != FinView::MINI_VIEW_PESSIMISTIC_ONLY)
        {
            this->drawAxesLabels(&painter);
        }
    }
}

void FinView::mousePressEvent(QMouseEvent *event)
{
}

void FinView::portfolioChanged()
{
    this->update();
}

void FinView::increasePlotBounds()
{
    if((maxBound - minBound) <= 10000000)
    {
        int changeAmount = (maxBound - minBound) / 2;
        minBound -= changeAmount;
        maxBound += changeAmount;
        this->update();
    }
}

void FinView::decreasePlotBounds()
{
    if((maxBound - minBound) % 4 == 0)
    {
        int changeAmount = (maxBound - minBound) / 4;
        minBound += changeAmount;
        maxBound -= changeAmount;
        this->update();
    }
}

int FinView::drawFinalOutcome()
{
    return this->drawOutcome(true);
}

int FinView::drawSampleOutcome()
{
    return this->drawOutcome(false);
}

void FinView::drawOutcome(int sampleOutcome)
{
    sampleOutcomeShown = true;

    if(displayType == FinView::TABLE_VIEW)
    {
        sampleOutcomeShown = false;
    }

    this->sampleOutcome = sampleOutcome;

    this->update();
}

int FinView::drawOutcome(bool finalResult)
{
    double p;
    double sampleResultValue;

    int negative = 1;
    if(rand() % 2 == 0)
        negative = -1;

    p = rand() / (double) RAND_MAX;

    for(int i = 0; i < 401; i++)
    {
        if(precalculatedGaussianCDF[i][1] > p)
        {
            sampleOutcome = precalculatedGaussianCDF[i][0];//(negative * (i - 1)) / 200.0;
            break;
        }
    }

    if(sampleOutcome == 0 || scenarioPortfolio.investmentCount() == 0)
        sampleResultValue = scenarioPortfolio.expectedWealth(MAX_YEARS);
    else if(sampleOutcome < 0)
        sampleResultValue = -sampleOutcome * scenarioPortfolio.pessimisticWealth(MAX_YEARS, 0) + (1.0 + sampleOutcome) * scenarioPortfolio.expectedWealth(MAX_YEARS);
    else if(sampleOutcome > 0)
        sampleResultValue = sampleOutcome * scenarioPortfolio.optimisticWealth(MAX_YEARS, 0) + (1.0 - sampleOutcome) * scenarioPortfolio.expectedWealth(MAX_YEARS);

    //printf("%f\n", sampleOutcome);

    sampleOutcomeShown = true;

    if(finalResult)
    {
        recorder.recordAction("FinalResult", sampleResultValue);
        QString scenario;
        if(finishedScenarios[currentScenario] == 0)
            scenario = "Blue";
        else if(finishedScenarios[currentScenario] == 1)
            scenario = "Green";
        else
            scenario = "Purple";

        QMessageBox *showSampleResult = new QMessageBox(QMessageBox::Information, "Final Result", tr("Outcome: $") + QString::number(sampleResultValue) + tr("\nYour Profit: $") + QString::number(sampleResultValue - 1000), QMessageBox::Ok);
        showSampleResult->exec();
    }
    else if(displayType == FinView::TABLE_VIEW)
    {
        sampleOutcomeShown = false;

        QMessageBox *showSampleResult = new QMessageBox(QMessageBox::Information, "Sample Result", tr("Sample wealth: $") + QString::number(sampleResultValue), QMessageBox::Ok);
        showSampleResult->exec();
    }

    if(!finalResult)
    {
        recorder.recordAction("SampleResult", sampleResultValue);
    }

    this->update();

    return this->sampleOutcome;
}

void FinView::reset()
{
    this->sampleOutcomeShown = false;
}

void FinView::enableSymbol(enum::symbols symbol)
{
    switch (symbol)
    {
        case SymbolGradient: showGradient = true; break;
        case SymbolSelected: showSelected = true; break;
        case SymbolUnselected: showUnselected = true; break;
        case SymbolLiquidAssets: showLiquidAssets = true; break;
        case SymbolBoundary: showBoundary = true; break;
        case SymbolResult: showResult = true; break;
        default: break;
    }

    this->update();
}

void FinView::disableSymbol(enum::symbols symbol)
{
    switch (symbol)
    {
        case SymbolGradient: showGradient = false; break;
        case SymbolSelected: showSelected = false; break;
        case SymbolUnselected: showUnselected = false; break;
        case SymbolLiquidAssets: showLiquidAssets = false; break;
        case SymbolBoundary: showBoundary = false; break;
        case SymbolResult: showResult = false; break;
        default: break;
    }

    this->update();
}
